import { getToken, validate } from './totp';
import { bigMultiply, bigNumberAdd, findUp, findDown } from './utils/fermat';

/**
 * 生成一个code
 * @param params prefix string 前缀
 * @param params secret string 密钥
 * @param params digits number 验证码位数
 * @param params interval number 验证码有效时间
 * @param params num number 验证数字（自动查找其临近的质数）
 * @param params type string 质数查找方式（up/down）up:向上查找,小于该数的最大质数 down:向下查找，大于该数的最小质数
 * @param params data number 携带的信息，比如用户ID
 * @returns string
 */
export function getCode(params = { prefix: '30', secret: 'getCode999', digits: 10, interval: 60, num: 9999, data: 1, type: 'down' }): string {
  const { prefix, secret, digits, interval, num, data, type } = params;
  if (data > num) {
    throw new Error('携带信息data必须小于验证数字num');
  }
  //   取得动态口令
  const token = getToken({ secret, digits, interval });
  //   获取质数
  const prime = type === 'up' ? findUp(num) : findDown(num);
  if (!prime) {
    throw new Error('质数不存在');
  }
  //   code = token * 质数 +  用户ID
  const bigMulNumber = bigMultiply(token, String(prime));
  const code = bigNumberAdd(bigMulNumber, String(data));
  return prefix + code;
}

/**
 * 校验code
 * @param code string 验证码
 * @param config prefix string 前缀
 * @param config secret string 密钥
 * @param config digits number 验证码位数
 * @param config interval number 验证码有效时间
 * @param config num number 验证数字（自动查找其临近的质数）
 * @param config type string 质数查找方式（up/down）up:向上查找,小于该数的最大质数 down:向下查找，大于该数的最小质数
 * @param config data number 携带的信息，比如用户ID
 * @returns string
 */
export function validateCode(code: string, config = { prefix: '30', secret: 'getCode999', digits: 10, interval: 60, num: 9999, type: 'down' }) {
  const { prefix, num, type } = config;
  code = code.replace(prefix, '');
  //   获取质数
  const prime = type === 'up' ? findUp(num) : findDown(num);
  if (!prime) {
    throw new Error('质数不存在');
  }
  //   data = code % prime
  const data = Number(code) % prime;
  //   token = code / prime
  const token = Math.floor(Number(code) / prime);
  //   校验动态口令
  const isToken = validate(String(token), config);
  const message = isToken ? 'success' : 'fail';
  if (isToken) {
    //   校验成功
    return { message, data, token };
  } else {
    //   校验失败
    return { message };
  }
}
